
#include "session.h"
#ifdef CONFIG_MEDIA_EMULATE_ON_PC
#include "ff/ff.h"
#include <curses.h>
#include <fcntl.h>
#else
#include "ff.h"
#include <io/keymap.h>
#endif

////////////////////////////////////////////////////////////////////
//
#define TEST_WITH_AUDIO
#define TEST_WITH_VIDEO
#define TEST_WITH_MOVIE

#ifndef CONFIG_MEDIA_EMULATE_ON_PC
#define TEST_USE_USB0
//#define TEST_USE_NETDISK
#endif

//#define TEST_GETKEY_BLOCKING
//#define TEST_FORWARD_REWIND
//#define TEST_FORWARD_REWIND_AUTO

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
#define ANSI_SP		0x20
#define ANSI_A		0x41
#define ANSI_a		0x61
#define VSK_LEFT	0x44
#define ANSI_d		0x64
#define VSK_RIGHT	0x43
#endif

////////////////////////////////////////////////////////////////////,
//

static const
session_file_t STREAMS_FILEs[] =
{
	{_TEXT("streams/h264aac_1s.mp4\0"), SST_MP4, {SST_H264, 17}, {SST_AAC_ADTS, 18, 2, 44100, 16}},
	{_TEXT("streams/h264aac_1s.mkv\0"), SST_MKV, {SST_H264, 17}, {SST_AAC_ADTS, 18, 2, 44100, 16}},
	{_TEXT("streams/h264_1s.h264\0"), SST_H264, {SST_H264, 17}, {0}},
	{_TEXT("streams/aac44100_1s.aac\0"), SST_AAC_ADTS, {0}, {SST_AAC_ADTS, 18, 2, 44100, 16}},

	// {_TEXT("/mnt/hgfs/video/input2hevcmp3.mp4\0"), SST_MP4, {SST_HEVC, 17}, {SST_MP3, 18, 2, 48000, 16}},
	// {_TEXT("/mnt/hgfs/video/Demo2020_short_720p.mp4\0"), SST_MP4, {SST_H264, 17}, {SST_AAC_ADTS, 18, 2, 48000, 16}},
};

///////////////////////////////////////////////////////////////////////////////////////////////
// SYS
extern struct SESSION gSession_sys;
// LCDI
extern struct SESSION gSession_display;
// I2S OUT
extern struct SESSION gSession_i2sout;
// USB
#ifdef TEST_USE_USB0
extern struct SESSION gSession_usb;
#endif
// Mali normal decoder
extern struct SESSION gSession_malidecoder;
// TS_HA
extern struct SESSION gSession_tsha;
// XA_AAC
extern struct SESSION gSession_xaaac;
// XA_MP3
extern struct SESSION gSession_xamp3;
// WAV
extern struct SESSION gSession_wav;
// MP4_DEMUXER
extern struct SESSION gSession_mp4;
// MKV_DEMUXER
extern struct SESSION gSession_mkv;

///////////////////////////////////////////////////////////////////////////////////////////////
static const struct SESSION *gSessions[]=
{
	// system & devices
	&gSession_sys,

#ifdef TEST_USE_USB0
	&gSession_usb,
#endif

#ifdef TEST_WITH_AUDIO
	&gSession_i2sout,
	&gSession_xaaac,
	&gSession_xamp3,
	&gSession_wav,
#endif

#ifdef TEST_WITH_VIDEO
	&gSession_display,
	&gSession_malidecoder,
#endif

#ifdef TEST_WITH_MOVIE
	&gSession_tsha,
	&gSession_mp4,
	&gSession_mkv,
#endif

	// end
	NULL
};

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
/**
 *  map_reset_buffers
 */


void ff_reset_buffers(int group)
{
	sysbuf_group_t gp;

	switch (group)
	{
	case SYSBUF_GROUP_CV_FRMBUFS:
	{
		gp.type = SYSBUF_GROUPTYPE_DDR;
		gp.vaddr = 0;
		gp.haddr = (phys_addr_t)malloc(CV_REFBUFS_CONT * CV_REFBUF_SIZE);
		gp.count = CV_REFBUFS_CONT;
		gp.unitsize = CV_REFBUF_SIZE;
	}break;
	case SYSBUF_GROUP_CV_BITBUFS:
	{
		gp.type = SYSBUF_GROUPTYPE_DDR;
		gp.vaddr = (phys_addr_t)malloc(CV_BITBUFS_CONT * CV_BITBUFS_SIZE);
		gp.haddr = gp.vaddr;
		gp.count = CV_BITBUFS_CONT;
		gp.unitsize = CV_BITBUFS_SIZE;
	}break;
	case SYSBUF_GROUP_DATBUFS:
	{
		gp.type = SYSBUF_GROUPTYPE_DDR;
		gp.vaddr = (phys_addr_t)malloc(DATBUF_MAX_BUF_CNT * DATBUF_MAX_BUF_LEN + 16);
		gp.haddr = (phys_addr_t)gp.vaddr;
		gp.count = DATBUF_MAX_BUF_CNT;
		gp.unitsize = DATBUF_MAX_BUF_LEN;
	}break;
	default:
		return;
	};

	sysbuf_group_reset(&gp, group);

}

static void map_reset_buffers(void)
{
	sysbuf_init();

	ff_reset_buffers(SYSBUF_GROUP_CV_BITBUFS);
	ff_reset_buffers(SYSBUF_GROUP_CV_FRMBUFS);
	ff_reset_buffers(SYSBUF_GROUP_DATBUFS);
}

#define FF_TCHAR char
#endif

static int GetFsDriverPath(storage_type_t type, FF_TCHAR path[])
{
	if (type == STORAGE_TYPE_USB0) {
		path[0] = 'u';
		path[1] = 's';
		path[2] = 'b';
		path[3] = '0';
		path[4] = ':';
		path[5] = 0;
		return 5;
	}

	else if (type == STORAGE_TYPE_USB1) {
		path[0] = 'u';
		path[1] = 's';
		path[2] = 'b';
		path[3] = '1';
		path[4] = ':';
		path[5] = 0;
		return 5;
	}

	return 0;

}

/* Full file name */
static int GetFsFullPathName(storage_type_t type, FF_TCHAR *pFull, int maxl, const FF_TCHAR *name)
{
	int res = GetFsDriverPath(type, pFull);
	if (res < 0)
		return res;

	if (name != NULL) {
		int j = 0;
		while(res < maxl && name[j]) {
			pFull[res++] = name[j++];
		}
		pFull[res] = 0;
	}

	return res;
}

#ifndef CONFIG_MEDIA_EMULATE_ON_PC
#if FF_USE_LFN && FF_LFN_UNICODE == 1 	/* Unicode in UTF-16 encoding */
#define Sprintf swprintf
#elif FF_USE_LFN && FF_LFN_UNICODE == 2	/* Unicode in UTF-8 encoding */
#define Sprintf snprintf
#else
#error Wrong FF_LFN_UNICODE setting
#endif
#endif

/**
 * get keyboard value and control puase, fast forward and fast rewind
 */
#ifdef TEST_GETKEY_BLOCKING
static uint32_t ccount_old;
#endif
static void get_key_value(struct SESSION *session)
{
	long key;
	int skip_time;
#ifdef TEST_GETKEY_BLOCKING
	uint32_t ccount;
	static unsigned long tmtick = 1;
#endif
	static int play_pause_flag = 0;
#ifdef TEST_FORWARD_REWIND_AUTO
	static int forward_rewind_auto_flag = 0;
#endif

	key = 0;

#ifdef TEST_GETKEY_BLOCKING
	/* wait delay */
	if (tmtick == 0) {
		ccount = media_SysGetMs();
		tmtick = 1;
		ccount_old = ccount;

#ifdef TEST_FORWARD_REWIND_AUTO
		forward_rewind_auto_flag++;
		if (forward_rewind_auto_flag % 2)
			key = VSK_RIGHT;
		else
			key = VSK_LEFT;
#else /* not TEST_FORWARD_REWIND_AUTO */
retry:
		key = getchar(); //getkey();
#ifdef CONFIG_MEDIA_EMULATE_ON_PC
		if (key != ANSI_SP && key != ANSI_A && key != ANSI_a
#else
		if (key != ANSI_SP && key != ANSI_A && key != ANSI_a && key != ANSI_D
#endif
					&& key != ANSI_d && key != VSK_LEFT && key != VSK_RIGHT)
			goto retry;
#endif /* TEST_FORWARD_REWIND_AUTO */
	}

	/* delay period of time */
	if (tmtick > 0) {
		ccount = media_SysGetMs();
		tmtick += (uint32_t)(ccount - ccount_old);
		ccount_old = ccount;

		if (tmtick >= 5000) { // unit: ms
			tmtick = 0;
		}
	}
#else /* not TEST_GETKEY_BLOCKING */
	key = getch(); // get key non-blocking
#endif /* TEST_GETKEY_BLOCKING */

	/* control pause, continue, fast forward and fast rewind */
	if (key > 0) {
		switch (key) {
		case ANSI_SP:	// space
			debug("********Press 'space'********\n");
			if (!play_pause_flag) {
				SessionCommand(session, SSCMD_STREAM_PAUSE, NULL);
				play_pause_flag = 1;
			} else {
				SessionCommand(session, SSCMD_STREAM_RESUME, NULL);
				play_pause_flag = 0;
			}
			break;
		case ANSI_A:	// 'A'
			//debug("Press 'A'\n");
		case ANSI_a:	// 'a'
			//debug("Press 'a'\n");
		case VSK_LEFT:	// left arrow
			debug("========Press 'left'========\n");
			skip_time = -5 * 1000; // unit: ms
			SessionCommand(session, SSCMD_STREAM_SKIP, &skip_time);
			break;
#ifndef CONFIG_MEDIA_EMULATE_ON_PC
		case ANSI_D:	// 'D'
			//debug("Press 'D'\n");
#endif
		case ANSI_d:	// 'd'
			//debug("Press 'd'\n");
		case VSK_RIGHT:	// right arrow
			debug("========Press 'right'========\n");
			skip_time = 5 * 1000; // unit: ms
			SessionCommand(session, SSCMD_STREAM_SKIP, &skip_time);
			break;
		default:
			//debug("Press other key 0x%x\n", key);
			break;
		}
	}

}

/* main */
int main(int argc, char * const argv[])
{
#ifdef TEST_USE_USB0
	const storage_type_t ftype = STORAGE_TYPE_USB0; // STORAGE_TYPE_NETFS
#endif
	int index, res;
	struct SESSION *session;
	session_file_t *file, filestream;
#ifdef MEDIA_AV_SYNC
	session_av_sync sync;
#endif
	SESSIONSTATE state;

	FF_TCHAR filename[MAX_PATH];

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
#ifdef TEST_FORWARD_REWIND
	// for getch()
	initscr();
	fcntl(0, F_SETFL, O_NONBLOCK);
	noecho();
#endif
#endif

    /* init buffer first */
    map_reset_buffers();

	/* start utp process */
#ifndef CONFIG_MEDIA_EMULATE_ON_PC
	UtpApp_Msg_Client_Init();
#endif

	/* init */
	SessionInitAll(gSessions);

	/* stream shift */
	index = -1;
	while(1)
	{
		/* file shift */
		index++;
		if (index >= sizeof(STREAMS_FILEs) / sizeof(STREAMS_FILEs[0])) {
			index = 0;
		}
		file = (session_file_t *)&STREAMS_FILEs[index];

		debug("\n\nSwitch to new stream %d\n", index);
		debug("filename: %s\n", file->filename);

#ifndef CONFIG_MEDIA_EMULATE_ON_PC
		GetFsFullPathName(ftype, filename, MAX_PATH, (const FF_TCHAR *)(file->filename));
#endif

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
		filestream.filename = file->filename;
#else
		filestream.filename = (void*)filename;
#endif
		filestream.type = file->type;
		filestream.tv = file->tv;
		filestream.ta = file->ta;
#ifndef CONFIG_MEDIA_EMULATE_ON_PC
		filestream.ftype = ftype;
#endif
#ifdef MEDIA_AV_SYNC
		filestream.sync = &sync;
#endif

		/* module checking */
		session = SessionGet(file->type);
		if (session == NULL) {
			debug("Can't find decoder module for stream %d, type = %d\n", index, (int)file->type);
			continue;
		}

		/* stream open */
		res = SessionCommand(session, SSCMD_STREAM_START, &filestream);
		if(res) {
			debug("Module command 'SSCMD_STREAM_START' error %d for stream %d, type = %d\n",
				res, index, (int)file->type);
			continue;
		}

		/* stream running */
#ifdef TEST_GETKEY_BLOCKING
		ccount_old = media_SysGetMs();
#endif
		do {
#ifdef TEST_FORWARD_REWIND
			get_key_value(session);
#endif
			state = SessionRun(session);
		} while(state == SSTATE_RUNNING || state == SSTATE_RUNNING_IDLE);

		/* stream close */
		res = SessionCommand(session, SSCMD_STREAM_STOP, NULL);
		if(res) {
			debug("Module command 'SSCMD_STREAM_STOP' error %d for stream %d, type = %d\n",
				res, index, (int)file->type);
			continue;
		}
	}

	/* deinit */
	SessionDeInitAll();

	/* stop utp process */
#ifndef CONFIG_MEDIA_EMULATE_ON_PC
	UtpApp_Msg_Client_DeInit();
#endif

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
#ifdef TEST_FORWARD_REWIND
	endwin(); // for getch()
#endif
#endif

	return 0;
}
